import flash.geom.Rectangle;
import com.troyworks.particleengine.PhysicsParticle;
import com.troyworks.framework.ui.DraggableClip;
import com.troyworks.framework.ui.layout.Bounds2;
import com.troyworks.geom.d2.Rect2D;
import com.troyworks.framework.ui.layout.LayoutHelper;
import com.troyworks.geom.d2.Point2D;
/**
 * @author Troy Gardner
 */
class com.troyworks.particleengine.ParticleSim extends MovieClip {

	var bounds : Rect2D;
	var layoutMan : LayoutHelper;

	var particles : Array = new Array();
	var clips : Array = new Array();

	var numOfParticles : Number = 0;
	
	//////////Forces ///////////
	var easing : Number = .1;
	var bounce : Number = 0.7;
	var gravity : Number = 1;
	var spring : Number = 0.1;
	var springLength : Number = 75;
	var friction : Number = 0.97;
	var linkageID : String = "ball";
	public function ParticleSim() {
		
	}
	public function onLoad() : Void{
		trace("onLoad");
		bounds = new Rect2D();
		bounds.top = 0;
		bounds.left = 0;
		bounds.bottom =_height;
		bounds.right = _width;
		layoutMan = new LayoutHelper(null, bounds);
		trace(" bounds " + util.Trace.me(bounds, "bounds", true));
		createParticles();

	}
	function createParticles() : Void{
		layoutMan.setupLayout(numOfParticles);
		for (var i : Number = 0; i <numOfParticles; i++) {
			var p2d : Point2D = new Point2D();
			layoutMan.layoutClip(i, p2d);

			var p : PhysicsParticle = new PhysicsParticle();
			p.cp.x = p2d.x;
			p.cp.y = p2d.y;

			p.v.x = Math.random() * 10 -5;
			p.v.y = Math.random() * 10 -5;

			particles.push(p);
			var initO : Object = new Object();
			initO._x = p.x;
			initO._y = p.y;
			initO.p = p;
			initO.id = i;
			var _mc : MovieClip = attachMovie(linkageID,linkageID+i, i+10, initO);
			p.view = _mc;
			clips.push(_mc);
		}
	}
	function onEnterFrame():Void
	{
		for(var i:Number = 0;i<numOfParticles;i++)
		{
			var particle:MovieClip = this["p" + i];
			particle._x += particle.vx;
			particle._y += particle.vy;
		}
		for(i=0;i<numOfParticles-1;i++)
		{
			var partA:PhysicsParticle = this["p" + i];
			for(var j:Number = i+1;j<numOfParticles;j++)
			{
				var partB:PhysicsParticle = this["p" + j];
				checkCollision(partA, partB);
				gravitate(partA, partB);
			}
		}
	}

	function springTo(pA : PhysicsParticle, pB : PhysicsParticle) : Void{
		trace("springTo");
		var dx : Number = pA.x - pB.x;
		var dy : Number = pA.y - pB.y;
		var angle : Number = Math.atan2(dy, dx);
		var targetX : Number = pB.x + Math.cos(angle) * springLength;
		var targetY : Number = pB.y + Math.sin(angle) * springLength;
		pA.v.x  += (targetX - pA.x) * spring;
		pA.v.y  += (targetY - pA.y) * spring;
		pA.v.x *= friction;
		pA.v.y *= friction;
	}
	function gravitate(pA : PhysicsParticle, pB : PhysicsParticle) : Void{
		trace("gravitate");
		var dx : Number = pA.x - pB.x;
		var dy : Number = pA.y - pB.y;
		var distSQ : Number = dx*dx + dy*dy;
		var dist : Number = Math.sqrt(distSQ);
		var force : Number = pA.mass * pB.mass /distSQ;
		var ax : Number = force * dx /dist;
		var ay : Number = force * dy /dist;
		pA.v.x += ax /pA.mass;
		pA.v.y += ay/ pA.mass;
		pB.v.x += ax /pB.mass;
		pB.v.y += ay/ pB.mass;
	}
	function drag(p : PhysicsParticle) : Void{
		var ball : DraggableClip = DraggableClip(p.view);
		p.a.x = 0;
		p.a.y = 0;
		p.v.x = 0;
		p.v.y = 0;
//		p.v.x = ball._x - ball.lastX;
//		p.v.y = ball._y - ball.lastY;
		p.cp.x = ball._x;
		p.cp.y = ball._y;
	}
	function rotate(x : Number, y : Number, sine : Number, cosine : Number, reverse : Boolean) : Object
{
		var result : Object = new Object();
		if(reverse)
		{
			result.x = x * cosine + y * sine;
			result.y = y * cosine - x * sine;
		}
		else
		{
			result.x = x * cosine - y * sine;
			result.y = y * cosine + x * sine;
		}
		return result;
	}
	function checkCollision(ball0 : MovieClip, ball1 : MovieClip) : Void
{
		var dx : Number = ball1._x - ball0._x;
		var dy : Number = ball1._y - ball0._y;
		var dist : Number = Math.sqrt(dx*dx + dy*dy);
		if(dist < ball0._width / 2 + ball1._width / 2)
		{
			// calculate angle, sine and cosine
				var angle : Number = Math.atan2(dy, dx);
				var sine : Number = Math.sin(angle);
				var cosine : Number = Math.cos(angle);
			
			// rotate ball0's position
				var pos0 : Object = {x:0, y:0};
			
			// rotate ball1's position
				var pos1 : Object = rotate(dx, dy, sine, cosine, true);
	
			// rotate ball0's velocity
				var vel0 : Object = rotate(ball0.vx, ball0.vy, sine, cosine, true);
			
			// rotate ball1's velocity
				var vel1 : Object = rotate(ball1.vx, ball1.vy, sine, cosine, true);
	
			// collision reaction
				var vxTotal : Number = vel0.x - vel1.x;
				vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) / (ball0.mass + ball1.mass);
				vel1.x = vxTotal + vel0.x;
			
			// update position
				var absV : Number = Math.abs(vel0.x) + Math.abs(vel1.x);
				var overlap : Number = (ball0._width / 2 + ball1._width / 2) - Math.abs(pos0.x - pos1.x);
				pos0.x += vel0.x / absV * overlap;
				pos1.x += vel1.x / absV * overlap;
			
			// rotate positions back
				var pos0F : Object = rotate(pos0.x, pos0.y, sine, cosine, false);
	
				var pos1F : Object = rotate(pos1.x, pos1.y, sine, cosine, false);
			
			// adjust positions to actual screen positions
				ball1._x = ball0._x + pos1F.x;
				ball1._y = ball0._y + pos1F.y;
				ball0._x = ball0._x + pos0F.x;
				ball0._y = ball0._y + pos0F.y;
			
			// rotate velocities back
				var vel0F : Object = rotate(vel0.x, vel0.y, sine, cosine, false);
				var vel1F : Object = rotate(vel1.x, vel1.y, sine, cosine, false);
				ball0.vx = vel0F.x;
				ball0.vy = vel0F.y;
				ball1.vx = vel1F.x;
				ball1.vy = vel1F.y;
		}
	}
	function checkBounds(p : PhysicsParticle) : Void{
		var ball : DraggableClip = DraggableClip(p.view);
		var b : Bounds2 = new Bounds2(ball.getBounds(this));
			///////// check x ///////////////
			if(b.xMax > bounds.right){
			trace("hit R wall");
			p.cp.x = bounds.right - b.width;
			p.v.x = Math.abs(p.v.x)*-1* bounce;
			}else if(b.xMin < bounds.left){
			trace("hit L wall");
			p.cp.x = bounds.left + 1;// + b.width + 1;
			p.v.x = Math.abs(p.v.x) * bounce;
			}
			////////// check y ////////////////
			if( bounds.bottom -.9 < b.yMax && b.yMax < bounds.bottom +.9 && Math.abs(p.v.y) <3){
		//		trace("on Bottom");
			//	v.x = 0;
			   if(gravity >0){
				p.v.y = -gravity;
			   }
			} else if(b.yMax >bounds.bottom){
			trace("hit Bottom yMax: " + b.yMax  + " bottom: " + bounds.bottom + " v: " + p.v.y);

			p.v.y= Math.abs(p.v.y* bounce)*-1;
			p.cp.y -= (b.yMax- bounds.bottom) - p.v.y;
			}else if(b.yMin < bounds.top){
			trace("hit Top");
			p.cp.y = bounds.top +1 ;//+ b.height;
			p.v.y= Math.abs(p.v.y* bounce);
			}
			///// update the clip /////////
		ball._x = p.x;
		ball._y = p.y;
	}
	function applyGravity(p : PhysicsParticle) : Void{
		p.v.y += gravity;
	}
}